
// XXX TODO
//  X test opening a window
//  X port over widget URIs
//  X port over URI policy
//  X make some temporary UI that can install and load a widget
//  X open in a Panel which is the widgets manager
//  X configure to load application/widget
//  - get .wgt working
//  - make TWI work
//  - make WARP work
//  - unit test
//  - refactor to make sense and to clean code up
//  - go for full P+C compliance

// --- OBJECTS --------------------------------------------------------------------------------------
const nativeJSON = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON); 
const Widget = require("widget").Widget;
const xpcom = require("xpcom");

// --- SQL ------------------------------------------------------------------------------------------
const CREATE_TBL = "CREATE TABLE widgets (id INTEGER PRIMARY KEY, uuid TEXT, config TEXT);";
const INSERT_WID = "INSERT INTO widgets (uuid, config) VALUES(:uuid, :config)";
const DELETE_WID = "DELETE FROM widgets WHERE uuid = :uuid;";
const GET_ALL_WID = "SELECT uuid FROM widgets;";

// --- FIELDS ---------------------------------------------------------------------------------------
exports.VERSION = "0.4";
let initialised = false;
let widDir, widgetsDB;
exports.init = function init (callbacks) {
    this.callbacks = callbacks;
    if (initialised) return;
    exports.widgetCache = {};
    // install directory + init
    // XXX we want the ProfD but it currently fails so we use DefProfRt instead
    // on OSX it maps to /Users/robin/Library/Application Support/Test App/Profiles/widgets/
    let profileDir = Cc["@mozilla.org/file/directory_service;1"]
                         .getService(Ci.nsIProperties)
                         .get("DefProfRt", Ci.nsIFile);
                         // .get("ProfD", Ci.nsIFile);
                         // .get("TmpD", Ci.nsIFile);
                         
    widDir = profileDir.clone();
    widDir.append("widgets");
    if (!widDir.exists()) widDir.create(widDir.DIRECTORY_TYPE, 0777); // XXXX perms?
    exports.widDir = widDir;
    // widgets DB + init
    let dbFile = profileDir.clone(), mustCreate = false;
    dbFile.append("widgets.sqlite");
    if (!dbFile.exists()) mustCreate = true;
    widgetsDB = Cc["@mozilla.org/storage/service;1"]
                       .getService(Ci.mozIStorageService)
                       .openDatabase(dbFile);
    if (mustCreate) widgetsDB.executeSimpleSQL(CREATE_TBL);
    initialised = true;
};

// --- INSTALLED WIDGETS MANAGEMENT -----------------------------------------------------------------
exports.getPackage = function getPackage (uuid) {
    var widFile = widDir.clone();
    widFile.append(uuid + ".wgt");
    var zReader = Cc["@mozilla.org/libjar/zip-reader;1"].createInstance(Ci.nsIZipReader);
    zReader.open(widFile);
    return zReader;
};
exports.handleDownloadedWidget = function handleDownloadedWidget (file) {
    // here we should have some form of dialog confirming the installation
    // but for now we just forge ahead like madmen
    return this.installWidget(file);
};
exports.installWidget = function installWidget (file) {
    var uuid = Cc["@mozilla.org/uuid-generator;1"]
                     .getService(Ci.nsIUUIDGenerator)
                     .generateUUID()
                     .toString();
    uuid = uuid.replace("{", "").replace("}", "");

    // copy to location, then validate
    file.copyTo(widDir, uuid + ".wgt");
    var widget = this.createWidget(uuid);
    var config;
    try {
        config = widget.config();
    }
    catch (e) {
        this.removeWidget(uuid);
        return null;
    }

    // XXX note that config depends on locale, and will therefore need to be updated when locale is
    var stm = widgetsDB.createStatement(INSERT_WID);
    stm.params.uuid = uuid;
    stm.params.config = nativeJSON.encode(config);
    stm.execute();
    
    for (var i = 0, n = onInstallHandlers.length; i < n; i++) onInstallHandlers[i](widget);
    return widget;
};
let onInstallHandlers = [];
exports.onInstall = function onInstall (cb) {
    onInstallHandlers.push(cb);
};
exports.removeWidget = function removeWidget (uuid) {
    var widFile = widDir.clone();
    widFile.append(uuid + ".wgt");
    widFile.remove(false);
    var stm = widgetsDB.createStatement(DELETE_WID);
    stm.params.uuid = uuid;
    stm.execute();
};
exports.createWidget = function createWidget (uuid) {
    var cw = this.hitCache(uuid);
    if (cw) return cw;
    var wid = (new Widget()).initWithUUID(uuid);
    this.cacheWidget(uuid, wid);
    return wid;
};
exports.getWidgetList = function getWidgetList () {
    var stm = widgetsDB.createStatement(GET_ALL_WID);
    // stm.execute();
    var res = [];
    while (stm.executeStep()) {  
        res.push(this.createWidget(stm.row.uuid));
    }
    return res;
};

// --- APPLICATION ----------------------------------------------------------------------------------
exports.extendWindow = function extendWindow (win) {
    // win.widgeon = exports;
}
exports.loadManager = function loadManager () {
    var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].getService(Ci.nsIWindowWatcher);
    var uri = packaging.getURLForData("/manager.xhtml");
    // var args = {
    //     __exposedProps__: { test: "rw", wrappedJSObject: "rw"},
    //     test: "This is test content",
    // };
    this.manager = ww.openWindow(null, uri, "_blank", "chrome,centerscreen,resizable,dialog=no,width=500,height=600", {});
    this.extendWindow(this.manager);
    return this.manager;
};
exports.loadAboutManager = function loadAboutManager () {
    var fuel = Cc["@mozilla.org/fuel/application;1"].getService(Ci.fuelIApplication);
    // console.log("FUEL name: " + fuel.name);
    if (fuel.name === "Test App") {
        var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].getService(Ci.nsIWindowWatcher);
        this.manager = ww.openWindow(null, "about:widgets", "_blank", "chrome,centerscreen,resizable,dialog=no,width=500,height=600", {});
        this.extendWindow(this.manager);
    }
    else {
        try {
            // win = fuel.activeWindow;
            // var uri = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService).newURI("about:widgets", null, null);
            // var self = this;
            // newTab = Application.activeWindow.open(uri);
            // newTab.events.addListener("load", function() {
            //     console.log("new tab loaded!");
            //     self.manager = newTab.document.defaultView;
            //     self.extendWindow(self.manager)
            // });
        }
        catch (e) {
            console.log("Now active browser window? " + e);
        }        
    }
};
exports.browseForWidget = function browseForWidget () {
    var nsIFilePicker = Ci.nsIFilePicker;
    var fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
    fp.init(this.manager, "Select a widget", nsIFilePicker.modeOpen);
    fp.appendFilter("Widget Packages", "*.wgt");
    fp.appendFilter("Generic Packages", "*.zip; *.jar");
    fp.appendFilters(nsIFilePicker.filterAll);
    var res = fp.show();
    if (res == nsIFilePicker.returnOK) {
        return fp.file;
    }
    else {
        return null;
    }
}

// XXX this is a hack
exports.quit = function quit () {
    this.callbacks.quit();
};

// --- UTILS ----------------------------------------------------------------------------------------
exports.fileURI = function fileURI (file) {
    return Cc["@mozilla.org/network/protocol;1?name=file"]
                     .getService(Ci.nsIFileProtocolHandler)
                     .getURLSpecFromFile(file);
};
exports.cacheWidget = function cacheWidget (uuid, widget) {
    this.widgetCache[uuid.toLowerCase()] = widget;
};
exports.hitCache = function hitCache (uuid) {
    if (!this.widgetCache[uuid.toLowerCase()]) return null;
    return this.widgetCache[uuid.toLowerCase()];
};
// XXX this could use some improvements for sure
exports.supportsType = function supportsType (type) {
    type = type.toLowerCase();
    if (type == "text/html" || type == "text/xml" || type == "application/xml") return true;
    if (/\+xml$/.test(type)) return true;
    return false;
};
// XXX this is probably incorrect, we'll have to figure it out later
exports.supportsEncoding = function supportsEncoding (enc) {
    enc = enc.toLowerCase();
    if (enc == "utf-8" || enc == "utf8" || enc == "utf-16" || enc == "utf16") return true;
    if (/^iso-8859-/.test(enc)) return true;
    return false;
};

// --- EXPOSE ACCESS --------------------------------------------------------------------------------
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");

function WidgeonFinder () {}
WidgeonFinder.prototype = {
    getWidgeon:     function () {
        return exports;
    },
    classDescription:   "Widgeon Finder",
    classID:            Components.ID("534256d4-5680-4736-beba-5f0df7d86b7c"),
    contractID:         "@berjon.com/widgeon-finder;1",
    QueryInterface:     XPCOMUtils.generateQI([Ci.nsISupports])
};

xpcom.register({
    create:     WidgeonFinder,
    name:       WidgeonFinder.prototype.classDescription,
    contractID: WidgeonFinder.prototype.contractID,
    uuid:       WidgeonFinder.prototype.classID
});

